/*
** This file contains all sources (including headers) to the LEMON
** LALR(1) parser generator.  The sources have been combined into a
** single file to make it easy to include LEMON in the source tree
** and Makefile of another program.
**
** The author of this program disclaims copyright.
*/

/**
 * \file    mylemonpp.cc
 * \brief   Parser generator.
 * \author  Jérôme LAURENS
 * \version 0.1
 * \date    September 17, 2014
 *
 * Reads a LALR(1) grammar, generates a parser based on a template file.
 * This is based on lemon parser generator, but has strongly been
 * revisited.
 *
 */

#include "grammar.h"
#include "parser.h"
#if 0
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <assert.h>

#include <string>
#include <regex>
#include <vector>
#include <map>
#include <queue>
#include <deque>
#include <unordered_set>
#include <memory>
#include <sstream>
#include <iostream>
#include <fstream>
#include <forward_list>
#include <functional>


#include <boost/variant.hpp>
#include <boost/foreach.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/find.hpp>
#endif

/* #define PRIVATE static */
#define PRIVATE

#include "grammar.h"

typedef size_t ActionCode;

class LemonPP; // global object, owns the grammar



class LemonPP {
public:
    GrammarRef grammar_m;
    int number_of_errors;            /* Number of errors */

    String input_file_name;
    String output_file_name;
    String token_prefix;       /* A prefix added to token names in the .h file */
    size_t number_of_conflicts;
    int tablesize;           /* Size of the parse tables */
    bool print_only_core_Item;
    bool grammar_has_fallback_m;
    bool print_line_numbers;
    Index & line_number() { return line_number_m; }
#warning track line number
    Index line_number_m;
    StringVector defined_macro_names;  /* Name of the -D macros */

    LemonPP() {};

#if 0
    FILE *file_open(
            const char *suffix,
            const char *mode
           ) ;
    void Reprint();
    void ReportOutput();
    int compute_action(const action & ap) const;
    void translate_code(RuleRef rp);
    void emit_code(
            FILE *out,
            RuleRef rp,
            int *line_number_m
           ) ;
    void generateParserSourceCode();
    void CompressTables();
    void ResortStates();
    void FindStates();
    StateWrap  getstate();
    void FindLinks();
    void FindFollowSets();
    void FindActions();
    void build_next_states(const StateWrap &stateRef) ;
    bool is_macro(const String & str) const;
#endif
};

static String user_template_name;
static String out_extension;

#include <boost/program_options.hpp>
namespace bpo = boost::program_options;

static bool showPrecedenceConflict = 0;
#warning push this inside LemonPP

/* The main program.  Parse the command line and do it... */
int Xmain(int argc, char **argv)
{
    static bool dont_compress;
    static bool print_grammar;
    static bool quiet;
    static bool dont_sort_states;
    static bool do_not_print_line_numbers;
    static bool statistics;

    String grammar_file_name;
    StringVector defined_macro_names;

    LemonPP lem;

    bpo::options_description description("Allowed options");
    description.add_options()
        ("help,h"
            , "produce help message")
        ("version,v"
            , bpo::value<bool>()->default_value(false)
            , "Print the version number.")
        ("placeholders,p"
            , bpo::value<bool>()->default_value(false)
            ,"Print %% placeholder keys.")
        ("core-report,b"
            , bpo::value<bool>(&(lem.print_only_core_Item))->default_value(false)
            ,"Print only the core Item in report.")
        ("dont-compress,C"
            , bpo::value<bool>(&dont_compress)->default_value(false)
            ,"Don't compress the action table.")
        ("grammar,g"
            , bpo::value<bool>(&print_grammar)->default_value(false)
            , "Print grammar without actions.")
        ("no-line,L"
            , bpo::value<bool>(&do_not_print_line_numbers)->default_value(true)
            , "Do not print #line statements.")
        ("resolved-conflicts,r"
            , bpo::value<bool>(&showPrecedenceConflict)->default_value(false)
            , "Show conflicts resolved by precedence rules.")
        ("quiet,q"
            , bpo::value<bool>(&quiet)->default_value(false)
            , "Don't print the report file.")
        ("no-sort,R"
            , bpo::value<bool>(&dont_sort_states)->default_value(false)
            , "Do not sort or renumber states.")
        ("statistics,s"
            , bpo::value<bool>(&statistics)->default_value(false)
            , "Print parser statistics to standard output.")
        ("define,D"
            , bpo::value<StringVector>()
            ,"Define an %ifdef macro. Cumulative.")
        ("template,t"
            , bpo::value<String>(&user_template_name)
            , "Specify a template file.")
        ("input,i"
            , bpo::value<String>(&(lem.input_file_name))
            , "Specify a grammar input file.")
    ;
    bpo::positional_options_description pd;
    pd.add("input", 1);
    bpo::variables_map vm;
    bpo::store(bpo::parse_command_line(argc, argv, description), vm);
    bpo::notify(vm);

    if (vm.count("help")) {
        std::cout << description << std::endl;
        return 1;
    }
    if (vm.count("version,v")) {
        std::cout << "LemonPP version 0.1" << std::endl;
        return 2;
    }
    if (vm.count("placeholders,p")) {
        ;
    }

    if (vm.count("no-line,L")) {
        lem.print_line_numbers = !do_not_print_line_numbers;
    }

    lem.print_line_numbers = do_not_print_line_numbers==0;
    StringVector v = vm["define,D"].as<StringVector>();
    StringSet set(v.cbegin(),v.cend());
    Parser P(grammar_file_name,set);
    int exitcode;
    /* Initialize the machine */
  // DEBUG_MESSAGE("Parse the input file.");
    /* Parse the input file */
    GrammarRef g(Grammar::Make());
    Index number_of_errors = P.parse(g);
    if (number_of_errors) exit(number_of_errors);
    if (P.grammar()->rules().empty()) {
        std::cerr << "Empty grammar" << std::endl;
        exit(1);
    }

#if 0

    /* Count and index the symbols of the grammar */
    lem.grammarRef->symbolRefForName("{default}");// used to compress tables

    for(i=0; i<lem.number_of_symbols; ++i) lem.symbols[i]->index = i;


    /* Generate a reprint of the grammar, if requested on the command line */
    if (print_grammar) {
        lem.Reprint();
    } else {

        /* Find the precedence for every production rule (that has one) */
        lem.FindRulePrecedences();

        /* Compute the lambda_m-non terminals and the first-sets for every
    ** non terminal */
        lem.build_first_sets();

        /* Compute all LR(0) states.  Also record expected_symbols-set propagation
    ** links so that the expected_symbols-set can be computed later */
        lem.number_of_states = 0;
        lem.FindStates();

        /* Tie up loose ends on the propagation links */
        lem.FindLinks();

        /* Compute the expected_symbols set of every reducible Item */
        lem.FindFollowSets();

        /* Compute the action tables */
        lem.FindActions();

        /* Compress the action tables */
        if (!dont_compress) lem.CompressTables();

        /* Reorder and renumber the states so that states with fewer choices
    ** occur at the end.  This is an optimization that helps make the
    ** generated parser tables smaller. */
        if (dont_sort_states==0) lem.ResortStates();

        /* Generate a report of the parser generated.  (the "y.output" file) */
        if (!quiet) lem.ReportOutput();

        lem.generateParserSourceCode();

    }
    if (statistics) {
        printf("Parser statistics: %d terminals, %d non terminals, %d rules\n",
               lem.number_of_terminals, lem.number_of_symbols - lem.number_of_terminals, lem.number_of_rules());
        printf("                   %d states, %d parser table entries, %d conflicts\n",
               lem.number_of_states, lem.tablesize, lem.number_of_conflicts);
    }
    if (lem.number_of_conflicts > 0) {
        fprintf(stderr,"%d parsing conflicts.\n",lem.number_of_conflicts);
    } else {
        fprintf(stdout,"No parsing conflicts.\n");
    }

#endif
    /* return 0 on success, 1 on failure. */
    exitcode = ((lem.number_of_errors > 0) || (lem.number_of_conflicts > 0)) ? 1 : 0;
    exit(exitcode);
    return (exitcode);
}

#if 0


/* Every shift or reduce operation is stored as one of the expected_symbolsing */

class Grammar {
public:
    typedef Grammar __self;
    LemonPP & lemonPP_m;
    SymbolList symbolList; /* Sorted array of symbols */
    SymbolRefList terminalRefList; /* Sorted array of pointers to symbols */
    SymbolIndex number_of_symbols() {
        return std::distance(symbolList.cbegin(), symbolList.cend());
    }
    SymbolIndex number_of_terminals() {
        return std::distance(terminalRefList.cbegin(), terminalRefList.cend());
    }
    StringWrap2SymbolRefMap name2SymbolRefMap;

    RuleList ruleList;       /* List of all rules, (owner) */
    RuleIndex number_of_rules() {
        return std::distance(ruleList.cbegin(), ruleList.cend());
    }
    SymbolRef2RuleRefListMap lhsSymbolRef2RuleRefListMap;

    Symbol * error_symbol;   /* The error symbol */

    Symbol * wildcard_m; /* Token that matches anything */

    StateList stateList;   /* Table of states sorted by state number, owner */
    Index number_of_states() {
        std::distance(stateList.cbegin(), stateList.cend());
    };

    Grammar(LemonPP & __l)
        : lemonPP_m(__l) {
        symbol_for_name("$");
    }

    ActionCode accept_action_code() {
        return number_of_states() + number_of_rules()+1;
    }
    ActionCode error_action_code() {
        return accept_action_code()-1;
    }
    Symbol * existingsymbol_for_name(const String & name) const {
        StringWrap sR = std::cref(name);
        if (name2SymbolRefMap.count(sR)) {
            return name2SymbolRefMap[name];
        } else {
            return 0;
        }
#warning symbols_m -> name2SymbolRefMap
    };
    Symbol& symbol_for_name(const String & name) const;

    void build_first_sets();
};



/*
** Compilers are starting to complain about the use of sprintf() and strcpy(),
** saying they are unsafe.  So we define our own versions of those routines too.
**
** There are three routines here:  LemonPP_sprintf(), LemonPP_vsprintf(), and
** LemonPP_addtext().  The first two are replacements for sprintf() and vsprintf().
** The third is a helper routine for vsnprintf() that adds texts to the end of a
** buffer, making sure the buffer is always zero-terminated.
**
** The string description is a minimal subset of stdlib sprintf() supporting only
** a few simply conversions:
**
**   %d
**   %s
**   %.*s
**
*/
static void LemonPP_addtext(
        char *zBuf,           /* The buffer to which text is added */
        int *pnUsed,          /* Slots of the buffer used so far */
        const char *zIn,      /* Text to add */
        int nIn,              /* Bytes of text to add.  -1 to use strlen() */
        int iWidth            /* Field width.  Negative to left justify */
       ) {
    if (nIn<0) for(nIn=0; zIn[nIn]; ++nIn){}
    while (iWidth>nIn) { zBuf[(*pnUsed)++] = ' '; iWidth--; }
    if (nIn==0) return;
    memcpy(&zBuf[*pnUsed], zIn, nIn);
    *pnUsed += nIn;
    while ((-iWidth)>nIn) { zBuf[(*pnUsed)++] = ' '; ++iWidth; }
    zBuf[*pnUsed] = 0;
}
static int LemonPP_vsprintf(char *str, const char *zFormat, va_list ap){
    int i, j, k, c;
    int nUsed = 0;
    const char *z;
    char zTemp[50];
    str[0] = 0;
    for(i=j=0; (c = zFormat[i])!=0; ++i){
        if (c=='%') {
            int iWidth = 0;
            LemonPP_addtext(str, &nUsed, &zFormat[j], i-j, 0);
            c = zFormat[++i];
            if (isdigit(c) || (c=='-' && isdigit(zFormat[i+1]))) {
                if (c=='-') ++i;
                while (isdigit(zFormat[i])) iWidth = iWidth*10 + zFormat[i++] - '0';
                if (c=='-') iWidth = -iWidth;
                c = zFormat[i];
            }
            if (c=='d') {
                int v = va_arg(ap, int);
                if (v<0) {
                    LemonPP_addtext(str, &nUsed, "-", 1, iWidth);
                    v = -v;
                }else if (v==0) {
                    LemonPP_addtext(str, &nUsed, "0", 1, iWidth);
                }
                k = 0;
                while (v>0) {
                    ++k;
                    zTemp[sizeof(zTemp)-k] = (v%10) + '0';
                    v /= 10;
                }
                LemonPP_addtext(str, &nUsed, &zTemp[sizeof(zTemp)-k], k, iWidth);
            }else if (c=='s') {
                z = va_arg(ap, const char*);
                LemonPP_addtext(str, &nUsed, z, -1, iWidth);
            }else if (c=='.' && memcmp(&zFormat[i], ".*s", 3)==0) {
                i += 2;
                k = va_arg(ap, int);
                z = va_arg(ap, const char*);
                LemonPP_addtext(str, &nUsed, z, k, iWidth);
            }else if (c=='%') {
                LemonPP_addtext(str, &nUsed, "%", 1, 0);
            }else{
                fprintf(stderr, "illegal format\n");
                exit(1);
            }
            j = i+1;
        }
    }
    LemonPP_addtext(str, &nUsed, &zFormat[j], i-j, 0);
    return nUsed;
}
static int LemonPP_sprintf(char *str, const char *format, ...){
    va_list ap;
    int rc;
    va_start(ap, format);
    rc = LemonPP_vsprintf(str, format, ap);
    va_end(ap);
    return rc;
}

/********** From the file "build.h" ************************************/
void FindRulePrecedences();
void FindStates();
void FindLinks();
void FindFollowSets();
void FindActions();

/********** From the file "report.h" *************************************/
void Reprint(struct LemonPP *);
void ReportOutput(struct LemonPP *);
void generateParserSourceCode(struct LemonPP *, int);
void CompressTables(struct LemonPP *);
void ResortStates(struct LemonPP *);

#define NO_OFFSET (-2147483647)


/****************** From the file "action.c" *******************************/
/*
** Routines processing parser actions in the LEMON parser generator.
*/


/********************** New code to implement the "acttab" module ***********/
/*
** This module implements routines use to construct the yy_action[] table.
*/

/*********************** From the file "parse.c" ****************************/
/*
** Input file parser for the LEMON parser generator.
*/

/* Duplicate the input file without comments and without actions
** on rules */
void LemonPP::Reprint() {
    RuleRef rp;
    Symbol& sp;
    int i, j, maxlen, len, ncolumns, skip;
    printf("// Reprint of input file \"%s\".\n// Symbols:\n",input_file_name.c_str());
    maxlen = 10;
    for(i=0; i<number_of_symbols; ++i){
        sp = symbols[i];
        len = sp->name_m.length();
        if (len>maxlen) maxlen = len;
    }
    ncolumns = 76/(maxlen+5);
    if (ncolumns<1) ncolumns = 1;
    skip = (number_of_symbols + ncolumns - 1)/ncolumns;
    for(i=0; i<skip; ++i){
        printf("//");
        for(j=i; j<number_of_symbols; j+=skip){
            sp = symbols[j];
            assert (sp->index==j) ;
            printf(" %3d %-*.*s",j,maxlen,maxlen,sp->name_m);
        }
        printf("\n");
    }
    for (auto & rp: ruleList) {
#error use Rule description
        printf("%s",rp->lhs->name_m);
        /*    if (rp->lhs_alias_m) printf("(%s)",rp->lhs_alias_m); */
        printf(" ::=");
        for (auto & sp: rp->rhsSymbolRefList) {
            if (sp->type()==Symbol::Type::MULTITERMINAL) {
                printf(" %s", sp->sub_symbols_m.cbegin()->name_m);
                for (auto & subp: sp->sub_symbols_m) {
                    printf("|%s", subp->name_m);
                }
            }else{
                printf(" %s", sp->name_m);
            }
            /* if (rp->rhs_aliases()[i]) printf("(%s)",rp->rhs_aliases()[i]); */
        }
        printf(".");
        if (rp->precedence_symbol_m) printf(" [%s]",rp->precedence_symbol_m->name_m);
        if (rp->code) printf("\n    %s",rp->code);
        printf("\n");
    }
}




#endif
